package com.szl.group.config.aop;


import com.szl.group.common.anno.Retryable;
import com.szl.group.utils.JoinPointUtils;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

/**
 * description:
 * author: shenxiaolong
 * date: 2020/7/01  11:06
 */
@Aspect
@Component
@Slf4j
public class RetryAspect {

    @Pointcut("execution(public * com.szl.group.service..*.*(..)) &&"
            +
            "@annotation(com.szl.group.common.anno.Retryable)")
    public void retryPointcut() {
    }

    @Around("retryPointcut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {

        Retryable retryable = JoinPointUtils.getTargetAnnotation(point, Retryable.class);

        //1. 最大次数判断
        int maxAttempts = retryable.maxAttempts();
        if (maxAttempts <= 1) {
            return point.proceed();
        }

        //2. 异常处理
        int times = 0;
        final Class<? extends Throwable> exceptionClass = retryable.value();
        final long intervalTimes = retryable.intervalTimes();

        while (times < maxAttempts) {
            try {

                if (intervalTimes >= 0 && times > 1) {
                    Thread.sleep(intervalTimes);
                }
                return point.proceed();

            } catch (Throwable e) {
                times++;

                // 超过最大重试次数 or 不属于当前处理异常
                if (times >= maxAttempts ||
                        !exceptionClass.isAssignableFrom(e.getClass())) {
                    throw new Throwable(e);
                }
            }
        }
        return null;
    }
}
